分类
联系方式
  1. 新浪微博
  2. E-mail

Flutter Overlay

介绍

Overlay 是一个 Stack 的 widget,可以将 overlay entry 插入到 overlay 中,使独立的 child 窗口悬浮于其他 widget 之上。

因为 Overlay 本身使用的是 Stack 布局,所以 overlay entry 可以使用 Positioned 或者 AnimatedPositioned 在 overlay 中定位自己的位置。

当我们创建 MaterialApp 的时候,它会自动创建一个 Navigator,然后创建一个 Overlay; 然后利用这个 Navigator 来管理路由中的界面。

基础使用

引入 Overlay 组件

最小的工程代码如下:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: HomePage(),
  ));
}

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Overlay();
  }
}

此时会展示一个黑屏,但是 OverLay 已经引入了。

其中,MaterialApp 组件不可省略,要不然会报错 textDirection != null': is not true. 这说明 MaterialApp 中会初始化一些显示的基本配置。

initialEntries

这个属性用于初始化 Overlayout 默认展示元素。

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Overlay(
      initialEntries: [
        OverlayEntry(builder: (context) => Text("Hello"))
      ],
    );
  }
}

此时会在屏幕左上角出现一个不带主题的 Text。

向 Overlay 中插入元素

继续拓展上面的示例,将文本改为可点击,点击后向 Overlay 中插入一个新的 OverlayEntry:

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Overlay(
      initialEntries: [
        OverlayEntry(builder: (context) {
          return GestureDetector(
            child: Container(
              width: 200,
              height: 50,
              child: Text("弹出居中元素"),
            ),
            onTap: () {
              Overlay.of(context).insert(OverlayEntry(builder: (context) {
                return Center(child: Text("居中弹层"));
              }));
            },
          );
        })
      ],
    );
  }
}

效果如图:

无|缩略图

删除元素

再添加一个按钮,点击删除刚刚插入的弹层。有两种方法,一个是调用 Overlay 的 remove 方法,传入 OverlayEntry 示例。另一个方法是直接调用 OverlayEntry 实例的 remvoe 方法。

具体代码示例如下:

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Overlay(
      initialEntries: [
        OverlayEntry(builder: (context) {
          return Scaffold(
            appBar: AppBar(title: Text("首页")),
            body: MaterialButton(
              child: Text("弹出居中元素"),
              onPressed: () {
                OverlayEntry entry;
                entry = OverlayEntry(builder: (context) {
                  return Center(
                    child: Material(
                      child: Container(
                          color: Colors.pink,
                          width: 200,
                          height: 200,
                          child: Column(children: [
                            Text("居中弹层"),
                            MaterialButton(
                              child: Text('关闭'),
                              onPressed: () => entry.remove())
                          ]
                        ))));
                  }
                );
                Overlay.of(context).insert(entry);
              },
            ),
          );
        })
      ],
    );
  }
}

OverlayEntry 的 opaque 属性

如果 entry 不透明,overlay 为了提高效率,会不再构建下面的 entry,除非它们设置了 maintainState 属性。

在上面的示例中,在创建 OverlayEntry 时多传入一个 opaque 为 true。

此时中央弹层弹出时会展示如下效果: 无|缩略图

这是因为我们将 OverlayEntry 声明为不透明(其实 View 只占了中心部分空间),但是对于 Overlay 来说,只要 opaque,它就不再渲染底层组件,这中央中央弹层弹出时首页便消失了。点击关闭后首页重新出现。



主要就是两个方法,往 Overlay 中插入 entry,删除 Overlay 中的 entry。

//创建OverlayEntry
Overlay entry = new OverlayEntry(builder:(){/*在这里创建对应的widget*/});

//往Overlay中插入插入OverlayEntry

Overlay.of(context).insert(overlayEntry);

//调用entry自身的remove()方法,从所在的overlay中移除自己
entry.remove();

OverlayState

这是 Overlay 实现中的状态。

状态

名称 类型 说明
_entries List<OverlayEntry> 视图栈

OverlayEntry

Overlay 中的元素,包含一个 WidgetBuilder。通过 OverlayState.insert 方式进行插入操作。使用 Overlay.of 方法,获取到 BuildContext 中最近的 Overlay。一个 OverlayEntry 一次只能加入一个 Overlay。OverlayEntry 有一个 remove 方法,调用后会将自己从 Overlay 中删除。

由于 Overlay 使用 Stack 布局,OverlayEntry 可以使用 Positioned 和 AnimatedPositioned,实现在 Overlay 中定位。

构造

传入三个参数:

名称 类型 说明 备注
builder WidgetBuilder 视图构造器 调用 markNeedsBuild 可触发重复构建
_opaque bool 是否不透明 如果 entry 不透明,overlay 为了提高效率,会不再构建下面的 entry

除非它们设置了 maintainState 属性

maintainState bool 保持状态 这个 entry 是否必须要位于视图树中,即使有个不透明的盖住它

默认有不透明的,下面的就不渲染了。

对于 StatefulWidget 来说,这会导致它不被实例化(不走)

设置之后,就算不展示也会渲染。

这个操作开销大,小心使用。

特别是如果一个组件,设置了这个属性,在底下一直 setState,会增大耗电

maintainState

这个属性在 Navigator 和 Route 中使用,作用是当页面位于后台时(应该指非栈顶)。

这样页面的 Futures 才能保证运行完成后返回。

网络资源

Flutter之使用overlay显示悬浮控件

说说Flutter中的Overlay